﻿using System;
using System.IO;
using System.Linq;
using NDBGen.Configuration;

namespace NDBGen
{
    internal class Program
    {
        static void Main(String[] args)
        {
            // This allows a developer to attach to the ndbgenerate process for debugging.
            // 1. Run ndbgenerate.exe in its normal location with the -wait argument
            // 2. In Visual Studio, go to Debug -> Attach to Process
            // 3. Locate ndbgenerate.exe in the list and click Attach
            // 4. Assuming breakpoints have been set, press any key in the ndbgenerate window, breakpoints should be hit
            if (args.Contains("-wait"))
            {
                Console.Write("Press any key to start: ");
                Console.ReadKey();
                Console.WriteLine();
            }

            PluginLibraries.Init();

            String configPath = null;
            if (args.Contains("-config"))
            {
                var configArgIndex = Array.IndexOf(args, "-config");
                configPath = args.ElementAtOrDefault(configArgIndex + 1);
                if (configPath == null)
                {
                    Console.WriteLine("-config argument was specified, but could not find a path. Using default configuration path instead.");
                }
                else
                {
                    Environment.CurrentDirectory = new FileInfo(configPath).DirectoryName;
                }
            }
     
            try
            {
                var config = NDBGenSection.GetCurrent(configPath);

                var parser = new Parser();
                var parsedInput = parser.Parse(config.Input);
                var libraries = new PluginLibraries();

                DataAccess.Generate(config.Output.Schema.DataAccess, parsedInput, libraries);
                Database.GenerateSchema(config.Output.Schema.Database, parsedInput, libraries);
                Database.GenerateData(config.Output.Data, parsedInput, libraries);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                Console.WriteLine(ex.StackTrace);
                Environment.ExitCode = -1;
            }
            Console.WriteLine("All done!");
        }

        /*static void Run()
        {
            if (!File.Exists(fileConfig[Arg.SchemaSource])) { throw new FileNotFoundException(String.Format("{0} not found", fileConfig[Arg.SchemaSource])); }
            if (!File.Exists(fileConfig[Arg.OutputProject])) { throw new FileNotFoundException(String.Format("{0} does not exist", fileConfig[Arg.OutputProject])); }

            if (fileConfig.ContainsKey(Arg.LookupDataSource) && !File.Exists(fileConfig[Arg.LookupDataSource])) { throw new FileNotFoundException(String.Format("{0} not found", fileConfig[Arg.LookupDataSource])); }
            if (fileConfig.ContainsKey(Arg.SeedDataSource) && !File.Exists(fileConfig[Arg.SeedDataSource])) { throw new FileNotFoundException(String.Format("{0} not found", fileConfig[Arg.SeedDataSource])); }
            if (fileConfig.ContainsKey(Arg.FakeDataSource) && !File.Exists(fileConfig[Arg.FakeDataSource])) { throw new FileNotFoundException(String.Format("{0} not found", fileConfig[Arg.FakeDataSource])); }

            if (fileConfig.ContainsKey(Arg.PluginDir) && !Directory.Exists(fileConfig[Arg.PluginDir])) { throw new DirectoryNotFoundException(String.Format("{0} does not exist", fileConfig[Arg.PluginDir])); }

            Generator.Init(fileConfig.ContainsKey(Arg.PluginDir) ? fileConfig[Arg.PluginDir] : null);

            var generator = new Generator();

            var csharpRootPath = new FileInfo(fileConfig[Arg.OutputProject]).Directory.FullName;
            var projectFile = new VsProjectFile(fileConfig[Arg.OutputProject]);
            
            // Generate the SQL install file
            List<ScriptedTableData> lookupData = null;
            if (fileConfig.ContainsKey(Arg.LookupDataSource))
            {
                var lookupSource = File.ReadAllText(fileConfig[Arg.LookupDataSource]);
                lookupData = generator.ParseScriptedData(lookupSource);
            }

            List<ScriptedTableData> seedData = null;
            if (fileConfig.ContainsKey(Arg.SeedDataSource))
            {
                var seedSource = File.ReadAllText(fileConfig[Arg.SeedDataSource]);
                seedData = generator.ParseScriptedData(seedSource);
            }

            List<ScriptedTableData> fakeData = null;
            if (fileConfig.ContainsKey(Arg.FakeDataSource))
            {
                var fakeSource = File.ReadAllText(fileConfig[Arg.FakeDataSource]);
                fakeData = generator.ParseScriptedData(fakeSource);
            }

            var source = File.ReadAllText(fileConfig[Arg.SchemaSource]);
            var tables = generator.ParseTables(source, lookupData, seedData, fakeData);
            var sql = Generator.GenerateSql(tables);
            foreach (var key in sql.Keys)
            {
                CreateOrOverwriteFileIfNecessary(fileConfig[key], sql[key]);
            }

            projectFile.RemoveGeneratedFileReferences();

            // Generate the Linq-to-Sql classes
            foreach (var table in tables)
            {
                var csharp = table.GenerateCSharp(fileConfig[Arg.RootNamespace]);
                var dir = Path.Combine(csharpRootPath, table.Schema.ToPascalCase());
                if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);

                var fullFilePath = Path.Combine(dir, table.CSharpClassName);
                var relativeFilePath = Path.Combine(table.Schema.ToPascalCase(), table.CSharpClassName);
                
                var fullGeneratedFilePath = fullFilePath + ".g.cs";
                var relativeGeneratedFilePath = relativeFilePath + ".g.cs";

                String dependentFile;
                var mergeType = projectFile.GetMergeType(relativeFilePath, out dependentFile);
                switch (mergeType)
                {
                    case GeneratedMergeType.DoNotGenerate: continue;

                    case GeneratedMergeType.GeneratedOnly:
                        projectFile.AddFileReference(relativeGeneratedFilePath);
                        break;

                    case GeneratedMergeType.GeneratePartial:
                        projectFile.AddFileReference(relativeGeneratedFilePath, dependentFile);
                        break;
                }
                        
                CreateOrOverwriteFileIfNecessary(fullGeneratedFilePath, csharp);
            }

            // Generate the Linq-to-Sql DB classes
            var schemas = tables.Where(t => !t.WasReplacedByEnum).Select(t => t.Schema.Trim()).Distinct(StringComparer.InvariantCultureIgnoreCase);
            foreach (var schema in schemas)
            {
                var csharp = Generator.GenerateCSharpDataContext(tables.Where(t => t.Schema == schema && !t.WasReplacedByEnum).ToList(), fileConfig[Arg.RootNamespace]);
                var rootPath = Path.Combine(Path.Combine(csharpRootPath, schema.ToPascalCase()), "_DB.g.cs");
                projectFile.AddFileReference(Path.Combine(schema.ToPascalCase(), "_DB.g.cs"));
                CreateOrOverwriteFileIfNecessary(rootPath, csharp);
            }

            projectFile.Save();

            // Generate the obliterate file if necessary.
            if (fileConfig.ContainsKey(Arg.SqlObliterateOutput))
            {
                var obliterate = Generator.GenerateObliterate(tables);
                CreateOrOverwriteFileIfNecessary(fileConfig[Arg.SqlObliterateOutput], obliterate);
            }
        }*/
    }
}
